`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2021/04/14 09:36:11
// Design Name: 
// Module Name: I2cMaster_Top
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////
`include "i2c_master_define.v"

module I2cMaster_Top(
    input                                   sys_clk,
    input                                   sys_rst_n,
    input                                   sys_rst_i,
    // user-interface  signals
	input  [2:0]                            user_adr_i,     // lower address bits
	input  [7:0]                            user_dat_i,     // databus input
	output reg [7:0]                        user_dat_o,     // databus output
	input                                   user_we_i,      // write enable input
	input                                   user_stb_i,     // stobe/core select signal
	input                                   user_cyc_i,     // valid bus cycle input
	output reg                              user_ack_o,     // bus cycle acknowledge output
	output reg                              user_inta_o,    // interrupt request signal output    
    // I2C signals
	// i2c clock line
	input                                   scl_pad_i,       // SCL-line input
	output                                  scl_pad_o,       // SCL-line output (always 1'b0)
	output                                  scl_padoen_o,    // SCL-line output enable (active low) 
	// i2c data line
	input                                   sda_pad_i,       // SDA-line input
	output                                  sda_pad_o,       // SDA-line output (always 1'b0)
	output                                  sda_padoen_o     // SDA-line output enable (active low)
    );
 

// parameters
parameter ARST_LVL = 1'b0; // asynchronous reset level

//*************************variable declarations**************************************//
//i2c master internel registers
reg  [15:0]     prer;       // clock prescale register
reg  [ 7:0]     ctr;        // control register
reg  [ 7:0]     txr;        // transmit register
wire [ 7:0]     rxr;        // receive register
reg  [ 7:0]     cr;         // command register
wire [ 7:0]     sr;         // status register 
reg  [ 7:0]     dev_addr;   // i2c slave address
reg  [ 7:0]     reg_addr;   // i2c operate register addr
reg  [ 7:0]     i2c_data;   // i2c write data
// core enable signal
wire            core_en;
wire            int_en;
// status register signals
wire            irxack;
reg             rxack;       // received aknowledge from slave
reg             tip;         // transfer in progress
reg             irq_flag;    // interrupt pending flag
wire            i2c_busy;    // bus busy (start signal detected)
wire            i2c_al;      // i2c bus arbitration lost
reg             al;          // status register arbitration lost bit
wire            done;        // done signal: command completed, clear command register

 
// generate internal reset
wire sys_rst = sys_rst_i ^ ARST_LVL;   //by jt:means through ARST_LVL parameter and decide synchronous reset is high or low 

// generate user signals
wire user_wacc = user_we_i & user_ack_o;

// generate acknowledge output signal
always @(posedge sys_clk)
    user_ack_o <= #1 user_cyc_i & user_stb_i & ~user_ack_o; // because timing is always honored

// assign DAT_O  //by jt: read i2c controller register 
always @(posedge sys_clk)
    if(user_ack_o)
        begin
            case (user_adr_i) // synopsys parallel_case
                3'b000: user_dat_o <= #1 prer[ 7:0];
                3'b001: user_dat_o <= #1 prer[15:8];
                3'b010: user_dat_o <= #1 ctr;
                3'b011: user_dat_o <= #1 rxr; // write is transmit register (txr)
                3'b100: user_dat_o <= #1 sr;  // write is command register (cr)
                3'b101: user_dat_o <= #1 txr;
                3'b110: user_dat_o <= #1 cr;
                3'b111: user_dat_o <= #1 0;   // reserved
            endcase
        end 
    else
        user_dat_o <= #1 0;
    
// generate registers
//by jt:write vlaue to i2c register
always @(posedge sys_clk or negedge sys_rst_n)
    if (!sys_rst_n)
        begin
            prer     <= #1  16'hffff;
            ctr      <= #1  8'h0;
            //txr      <= #1  8'h0;
            dev_addr <= #1  8'h0;
            reg_addr <= #1  8'h0;
            i2c_data <= #1  8'h0;
        end
    else if (sys_rst)
        begin
            prer     <= #1  16'hffff;
            ctr      <= #1  8'h0;
            //txr      <= #1  8'h0;
            dev_addr <= #1  8'h0;
            reg_addr <= #1  8'h0;
            i2c_data <= #1  8'h0;
        end
    else if (user_wacc)
            case (user_adr_i) // synopsys parallel_case
                3'b000 : prer [ 7:0] <= #1 user_dat_i;
                3'b001 : prer [15:8] <= #1 user_dat_i;
                3'b010 : ctr         <= #1 user_dat_i;
                3'b011 : dev_addr    <= #1 user_dat_i;
                3'b100 : reg_addr    <= #1 user_dat_i;
                3'b101 : i2c_data    <= #1 user_dat_i;
                default: ;
            endcase    
 
// generate command register (special case)
always @(posedge sys_clk or negedge sys_rst_n)
    if (!sys_rst_n)
        cr <= #1 8'h0;
    else if (sys_rst_i)
        cr <= #1 8'h0;
    else if (user_wacc)
        begin
            if (core_en & (user_adr_i == 3'b110) )
                cr <= #1 user_dat_i;
        end
    else
        begin
            if (one_operation | i2c_al)
                cr[7:4] <= #1 4'h0;           // clear command bits when done
                                                        // or when aribitration lost
                cr[2:1] <= #1 2'b0;             // reserved bits
                cr[0]   <= #1 1'b0;             // clear IRQ_ACK bit
        end
             
// decode command register
reg start ;  //= cr[7];
reg stop ; //= cr[6];
reg read  ; //= cr[5];
reg write  ; //= cr[4];
reg ack ; //= cr[3];
wire iack ;//= cr[0];
     
// decode control register
assign i2c_read = cr[7];
assign i2c_write = cr[6];
assign core_en = ctr[7];
assign int_en = ctr[6]; 

//**********************i2c master read/write state machine turn round*******************************//
localparam    i2c_state_idle = 6'b000001;
localparam    i2c_devadd_wr  = 6'b000010;
localparam    i2c_regadd_wr  = 6'b000100;
localparam    i2c_txdata_wr  = 6'b001000;
localparam    i2c_rxdata_rd  = 6'b010000;
localparam    i2c_state_sto  = 6'b100000;
//state variable delclare
reg [5:0]   i2c_cs_state;
reg         one_operation;
always @(posedge sys_clk or negedge sys_rst_n)
    if (!sys_rst_n)
        begin
            i2c_cs_state <= i2c_state_idle;        txr <= 8'h00;    ack <= 1'b0;
            start <= 1'b0;   stop <= 1'b0;   read <= 1'b0;  write <= 1'b0;
            one_operation <= 1'b0;
        end       
    else if (sys_rst_i)
        begin
            i2c_cs_state <= i2c_state_idle;        txr <= 8'h00;    ack <= 1'b0;
            start <= 1'b0;   stop <= 1'b0;   read <= 1'b0;  write <= 1'b0;
            one_operation <= 1'b0;
        end        
    else 
        begin
            case(i2c_cs_state)
            i2c_state_idle:
                if(i2c_read | i2c_write)    //when system make a i2c_read or i2c_write instruction
                	begin //state machine run into i2c device addr write state
                    //meanwhile prepare device address and operate instruction 
                    	i2c_cs_state <= i2c_devadd_wr;		txr <= dev_addr & 8'hfe;  //the last bit is r/w enable bit
                    	start <= 1'b1;   stop <= 1'b0;   read <= 1'b0;  write <= 1'b1; 
                    end
                else
                    begin //otherwise keep same state and data and command are assign '0'
                        i2c_cs_state <= i2c_cs_state;       txr <= 8'h00;    
                        start <= 1'b0;   stop <= 1'b0;   read <= 1'b0;  write <= 1'b0;  
                        one_operation <= 1'b0;
                    end
            i2c_devadd_wr:
                if(done)    //when device address byte write finish(done is '1')
                    if(!irxack) //when slave acknowedge is '0'
                        begin //and state machine is trun into register addr write state
                        //meanwhile prepare register address and operate instruction
	                    	i2c_cs_state <= i2c_regadd_wr;	    txr <= reg_addr;  
	                    	start <= 1'b0;   stop <= 1'b0;   read <= 1'b0;  write <= 1'b1; 
                    	end
                    else //if irxack is '1',mean slave is not ack and state machine turn into idle state
                        i2c_cs_state <= i2c_state_idle;
                else //otherwise keep same state and data and command keep same value(last state)
                    i2c_cs_state <= i2c_cs_state; 

            i2c_regadd_wr: 
            	if(done)   //when register address byte write is finish(done is '1')
            		if(!irxack) //when slave acknowedge is '0'
                        begin //and state machine is trun into tranfer data state(write is data and read is device addr + 1'b0)
                        //meanwhile prepare write data or device addr and operate instruction
                            i2c_cs_state <= i2c_txdata_wr;   txr <= i2c_write ? i2c_data : (dev_addr | 8'h01);
                            start <= i2c_write ? 1'b0 : 1'b1;   stop <= 1'b0;   read <= 1'b0;   write <= 1'b1; 
                        end            			
                    else  //if irxack is '1',mean slave is not ack and state machine turn into idle state
                        i2c_cs_state <= i2c_state_idle;
                else //otherwise keep same state and data and command keep same vlaue(last state)
	                i2c_cs_state <= i2c_cs_state;     

            i2c_txdata_wr:
            	if(done)   //when transfer data byte write is finish(done is '1')
            		begin
            			if((!irxack) & i2c_write) //when slave acknowedge is '0' and i2c_write is enable
                            begin //and state machine is turn into stop state
                            //meanwhile prepare stop operate instruction
                                i2c_cs_state <= i2c_state_sto;   txr <= 8'h0;
                                start <= 1'b0;   stop <= 1'b1;   read <= 1'b0;   write <= 1'b0;
                                one_operation <= 1'b1; 
                            end
                        else if(!irxack & i2c_read) //when slave acknowedge is '0' and i2c_read is enable
                            begin //and state machine is turn into read data state
                            //meanwhile prepare read operate instruction
                                i2c_cs_state <= i2c_rxdata_rd;   txr <= 8'h0;    ack <= 1'b1;
                                start <= 1'b0;   stop <= 1'b0;   read <= 1'b1;   write <= 1'b0; 
                            end
                        else //if irxack is'1',mean slave is not ack and state machine turn into idle state
                            i2c_cs_state <= i2c_state_idle;
            		end
	            else //otherwise keep same state and data and command keep same value(last state)
	            	i2c_cs_state <= i2c_cs_state;  

            i2c_rxdata_rd:
            	if(done)
	                begin 
	                	i2c_cs_state <= i2c_state_sto;   txr <= 8'h0;
                        start <= 1'b0;   stop <= 1'b1;   read <= 1'b0;   write <= 1'b0;
                        one_operation <= 1'b1; 
	                end
	            else
	            	i2c_cs_state <= i2c_cs_state;  

            i2c_state_sto:
            	if(done)  //when stop operator is finish(done is '1')
            		begin //state machine is turn into idle state
            		//meanwhile data and command is initial and a flag(one operation) assert
            			i2c_cs_state <= i2c_state_idle; 	txr <= 8'h0;   ack <= 1'b0;
                        start <= 1'b0;   stop <= 1'b0;   read <= 1'b0;   write <= 1'b0; 
                        //one_operation <= 1'b1;
            		end
            	else//otherwise keep same state and data and command keep same value(last state)
            		i2c_cs_state <= i2c_cs_state ;

            default:
                begin
                	i2c_cs_state <= i2c_state_idle;    txr <= 8'h00;                       
                    start <= 1'b0;   stop <= 1'b0;   read <= 1'b0;  write <= 1'b0;
                    one_operation <= 1'b0;
                end
            endcase
        end

  
//************************hookup byte controller block********************************//
I2cMaster_ByteCtr   byte_controller (
    .sys_clk                    ( sys_clk           ),
    .sys_rst                    ( sys_rst           ),
    .sys_rst_n                  ( sys_rst_n         ),
    .core_en                    ( core_en           ),
    .clk_cnt                    ( prer              ),
    .start                      ( start             ),
    .stop                       ( stop              ),
    .read                       ( read              ),
    .write                      ( write             ),
    .ack_in                     ( ack               ),
    .din                        ( txr               ),
    .cmd_ack                    ( done              ),
   // .core_ack                   ( core_ack          ),  //by jt: add and delete
    .ack_out                    ( irxack            ),
    .dout                       ( rxr               ),
    .i2c_busy                   ( i2c_busy          ),
    .i2c_al                     ( i2c_al            ),
    .scl_i                      ( scl_pad_i         ),
    .scl_o                      ( scl_pad_o         ),
    .scl_oen                    ( scl_padoen_o      ),
    .sda_i                      ( sda_pad_i         ),
    .sda_o                      ( sda_pad_o         ),
    .sda_oen                    ( sda_padoen_o      )
    );  
    
// status register block + interrupt request signal
always @(posedge sys_clk or negedge sys_rst_n)
    if (!sys_rst_n)
        begin
            al       <= #1 1'b0;
            rxack    <= #1 1'b0;
            tip      <= #1 1'b0;
            irq_flag <= #1 1'b0;
        end
    else if (sys_rst)
        begin
            al       <= #1 1'b0;
            rxack    <= #1 1'b0;
            tip      <= #1 1'b0;
            irq_flag <= #1 1'b0;
        end
    else
        begin
            al       <= #1 i2c_al | (al & ~start);
            rxack    <= #1 irxack;
            tip      <= #1 (read | write);
            irq_flag <= #1 (done | i2c_al | irq_flag) & ~iack; // interrupt request flag is always generated
        end
         
// generate interrupt request signals
always @(posedge sys_clk or negedge sys_rst_n)
    if (!sys_rst_n)
        user_inta_o <= #1 1'b0;
    else if (sys_rst)
        user_inta_o <= #1 1'b0;
    else
        user_inta_o <= #1 irq_flag && int_en; // interrupt signal is only generated when IEN (interrupt enable bit is set)
         
// assign status register bits
assign sr[7]   = rxack;
assign sr[6]   = i2c_busy;
assign sr[5]   = al;
assign sr[4:2] = 3'h0; // reserved
assign sr[1]   = tip;
assign sr[0]   = irq_flag;
 
endmodule
